#include <QVariant>
#include <QDebug>
#include <QMetaObject>
#include <QMetaProperty>
#include <QDataStream>
#include <QFile>
#include <QSet>
#include <QQueue>
#include "ConfigDataModel.h"
#include "Items.h"

#define MAGIC_NUMBER 0xA1B2C3D4
#define CONFIG_DATA_VERSION 1

ConfigDataModel::ConfigDataModel(QObject *parent) : QObject(parent)
{
}

bool ConfigDataModel::readFromFile()
{
    return readFromFile(m_filePath);
}

bool ConfigDataModel::readFromFile(QString filePath)
{
    bool ret = true;
    QFile file(filePath);
    ret = file.open(QIODevice::ReadOnly);
    if (ret)
    {
        QDataStream in(&file);
        in.setVersion(QDataStream::Qt_5_6);
        quint32 magicNumber = 0;
        quint32 configDataVersion = 0;
        in >> magicNumber;
        in >> configDataVersion;
        if (magicNumber != MAGIC_NUMBER || configDataVersion != CONFIG_DATA_VERSION)
        {
            qWarning() << "版本不匹配";
            ret = false;
            return ret;
        }

        in >> *this;
    }
    return ret;
}

bool ConfigDataModel::writeToFile()
{
    return writeToFile(m_filePath);
}

bool ConfigDataModel::writeToFile(QString filePath)
{
    bool ret = true;
    QFile file(filePath);
    ret = file.open(QIODevice::WriteOnly);
    if (ret)
    {
        QDataStream out(&file);
        out.setVersion(QDataStream::Qt_5_6);
        out << quint32(MAGIC_NUMBER);
        out << quint32(CONFIG_DATA_VERSION);
        out << *this;
    }
    return ret;
}

void ConfigDataModel::resetAll()
{
    resetSelf();
    const QMetaObject *obj = this->metaObject();
    for (int i = obj->propertyOffset(); i < obj->propertyCount(); i++)
    {
        QMetaProperty property = obj->property(i);
        if (property.read(this).canConvert<ConfigDataModel*>())
        {
            ConfigDataModel *model = property.read(this).value<ConfigDataModel*>();
            if (model != nullptr)
            {
                model->resetAll();
            }
        }
    }
}

ConfigDataModel::~ConfigDataModel()
{
    const QMetaObject *obj = this->metaObject();
    for (int i = obj->propertyOffset(); i < obj->propertyCount(); i++)
    {
        QMetaProperty property = obj->property(i);
        if (property.read(this).canConvert<ConfigItem*>())
        {
            ConfigItem *item = property.read(this).value<ConfigItem*>();
            if (item != nullptr)
            {
                item->deleteLater();
            }
            property.write(this, QVariant::fromValue<ConfigItem*>(nullptr));
        }
        else if (property.read(this).canConvert<ConfigDataModel*>())
        {
            ConfigDataModel *model = property.read(this).value<ConfigDataModel*>();
            if (model != nullptr)
            {
                model->deleteLater();
            }
            property.write(this, QVariant::fromValue<ConfigDataModel*>(nullptr));
        }
    }
}


bool ConfigDataModel::ensureItemRegisterd()
{
    bool ret = true;
    const QMetaObject *obj = this->metaObject();
    for (int i = obj->propertyOffset(); i < obj->propertyCount(); i++)
    {
        QMetaProperty property = obj->property(i);
        if (property.read(this).canConvert<ConfigItem*>())
        {
            ConfigItem *item = property.read(this).value<ConfigItem*>();
            if (item == nullptr)
            {
                qWarning() << QString("ConfigItem(%1) %2 is pointed to null !").arg(property.typeName()).arg(property.name());
                ret = false;
            }
            else if (qobject_cast<ItemEnum*>(item) && !qobject_cast<ItemEnum*>(item)->getMetaEnum().isValid())
            {
                qWarning() << QString("ConfigItem(ItemEnum) %1 is not registered !").arg(property.name());
                ret = false;
            }
            else if (qobject_cast<ItemFlag*>(item) && !qobject_cast<ItemFlag*>(item)->getMetaEnum().isValid())
            {
                qWarning() << QString("ConfigItem(ItemFlag) %1 is not registered !").arg(property.name());
                ret = false;
            }
            // item->setObjectName(property.name());
        }
        else if (property.read(this).canConvert<ConfigDataModel*>())
        {
            ConfigDataModel *item = property.read(this).value<ConfigDataModel*>();
            if (item == nullptr)
            {
                qWarning() << QString("ConfigDataModel(%1) %2 is pointed to null !").arg(property.typeName()).arg(property.name());
                ret = false;
            }
        }


    }
    return ret;
}

void ConfigDataModel::resetSelf()
{

}


QDataStream &operator<<(QDataStream &stream, const ConfigDataModel &dataModel)
{
    QQueue<ConfigDataModel*> models;
    models.enqueue(const_cast<ConfigDataModel*>(&dataModel));
    while (!models.isEmpty())
    {
        ConfigDataModel* model = models.dequeue();
        const QMetaObject *obj = model->metaObject();
        stream << model->objectName();
        for (int i = obj->propertyOffset(); i < obj->propertyCount(); i++)
        {
            QMetaProperty property = obj->property(i);
            if (property.read(model).canConvert<ConfigItem*>())
            {
                ConfigItem *item = property.read(model).value<ConfigItem*>();
                if (item != nullptr)
                {
                    stream << item->objectName();
                    item->writeToData(stream);
                }
            }
            else if (property.read(model).canConvert<ConfigDataModel*>())
            {
                ConfigDataModel *item = property.read(model).value<ConfigDataModel*>();
                if (item != nullptr)
                {
                    models.enqueue(item);
                }
            }
        }
    }


    return stream;
}

QDataStream &operator>>(QDataStream &stream, ConfigDataModel &dataModel)
{
    QSet<ConfigDataModel*> models_set;
    QQueue<ConfigDataModel*> models_queue;
    models_queue.enqueue(const_cast<ConfigDataModel*>(&dataModel));
    while (!models_queue.isEmpty())
    {
        ConfigDataModel* model = models_queue.dequeue();
        models_set.insert(model);
        const QMetaObject *obj = model->metaObject();

        for (int i = obj->propertyOffset(); i < obj->propertyCount(); i++)
        {
            QMetaProperty property = obj->property(i);
            if (property.read(model).canConvert<ConfigDataModel*>())
            {
                ConfigDataModel *item = property.read(model).value<ConfigDataModel*>();
                if (item != nullptr)
                {
                    models_queue.enqueue(item);
                }
            }
        }
    }
    QString modelName;
    stream >> modelName;
    ConfigDataModel* model = nullptr;
    for (auto item : models_set)
    {
        if (item->objectName() == modelName)
        {
            model = item;
            break;
        }
    }
    if (model == nullptr)
    {
        return stream;
    }

    while (!stream.atEnd())
    {
        QString name;
        stream >> name;
        bool isModel = false;
        for (auto item : models_set)
        {
            if (item->objectName() == name)
            {
                isModel = true;
                model = item;
                break;
            }
        }
        if (isModel)
        {
            continue;
        }

        QVariant ptr = model->property(name.toUtf8().data());
        if (ptr.isValid() && ptr.canConvert<ConfigItem*>())
        {
            ConfigItem *item = ptr.value<ConfigItem*>();
            item->readFromData(stream);
        }

    }
    return stream;
}
